package com.lcq.demo.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lcq.demo.common.config.AWSConfig;
import com.lcq.demo.entity.dto.ThingShadowDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
import software.amazon.awssdk.core.retry.RetryPolicy;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iot.IotAsyncClient;
import software.amazon.awssdk.services.iot.IotClient;
import software.amazon.awssdk.services.iot.model.CreateThingRequest;
import software.amazon.awssdk.services.iot.model.CreateThingResponse;
import software.amazon.awssdk.services.iot.model.IotException;
import software.amazon.awssdk.services.iotdataplane.IotDataPlaneAsyncClient;
import software.amazon.awssdk.services.iotdataplane.model.GetThingShadowRequest;
import software.amazon.awssdk.services.iotdataplane.model.UpdateThingShadowRequest;
import software.amazon.awssdk.services.iotdataplane.model.UpdateThingShadowResponse;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;

/**
 * @author 240701005
 */
@Service
@Slf4j
public class IoTService {

    private static IotDataPlaneAsyncClient iotAsyncDataPlaneClient;

    private static IotAsyncClient iotAsyncClient;

    private IotDataPlaneAsyncClient getAsyncDataPlaneClient() {
        SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
                .maxConcurrency(100)
                .connectionTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .build();
        ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
                .apiCallTimeout(Duration.ofMinutes(2))
                .apiCallAttemptTimeout(Duration.ofSeconds(90))
                .retryPolicy(RetryPolicy.builder()
                        .numRetries(3)
                        .build())
                .build();
        if (iotAsyncDataPlaneClient == null) {
            iotAsyncDataPlaneClient = IotDataPlaneAsyncClient.builder()
                    .region(Region.US_EAST_1)
                    .httpClient(httpClient)
                    .overrideConfiguration(overrideConfig)
                    .build();
        }
        return iotAsyncDataPlaneClient;
    }

    private IotAsyncClient getIotAsyncClient() {
        SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder()
                .maxConcurrency(100)
                .connectionTimeout(Duration.ofSeconds(60))
                .readTimeout(Duration.ofSeconds(60))
                .writeTimeout(Duration.ofSeconds(60))
                .build();
        ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
                .apiCallTimeout(Duration.ofMinutes(2))
                .apiCallAttemptTimeout(Duration.ofSeconds(90))
                .retryPolicy(RetryPolicy.builder()
                        .numRetries(3)
                        .build())
                .build();
        if (iotAsyncClient == null) {
            iotAsyncClient = IotAsyncClient.builder()
                    .region(Region.US_EAST_1)
                    .httpClient(httpClient)
                    .overrideConfiguration(overrideConfig)
                    .build();
        }
        return iotAsyncClient;
    }

    /**
     * 创建事物
     */
    public void createThing(String thingName) {
        CreateThingRequest createThingRequest = CreateThingRequest.builder()
                .thingName(thingName)
                .build();
        CompletableFuture<CreateThingResponse> future = getIotAsyncClient().createThing(createThingRequest);
        future.whenComplete((createThingResponse, ex) -> {
           if (createThingResponse != null) {
               log.info("{} was successfully created. The ARN value is: {}", thingName, createThingResponse.thingArn());
           } else {
               Throwable cause = ex.getCause();
               if (cause instanceof IotException) {
                   log.error("Error creating thing: {}", ((IotException) cause).awsErrorDetails().errorMessage());
               } else {
                   log.error("Unexpected error: {}", cause.getMessage());
               }
           }
        });
    }


    /**
     * 获取设备影子信息
     */
    public ThingShadowDTO getDeviceShadow(String thingName) {
        ThingShadowDTO thingShadowDTO = new ThingShadowDTO();
        // 创建获取设备影子请求对象
        GetThingShadowRequest getThingShadowRequest = GetThingShadowRequest.builder()
                .thingName(thingName)
                .build();

        // 异步获取设备影子信息，并处理响应
        CompletableFuture<String> future = getAsyncDataPlaneClient().getThingShadow(getThingShadowRequest)
                .thenApply(getThingShadowResponse -> {
                    // 检查响应是否为空，如果为空抛出异常
                    if (getThingShadowResponse == null) {
                        throw new CompletionException(new Exception("获取设备影子信息失败"));
                    }
                    // 获取并转换影子信息的负载为UTF-8字符串
                    SdkBytes payload = getThingShadowResponse.payload();
                    String utf8String = payload.asUtf8String();
                    log.info("payload:{}", utf8String);
                    return utf8String;
                })
                .exceptionally(ex -> {
                    // 处理异常情况
                    Throwable cause = ex.getCause() != null ? ex.getCause() : ex;
                    // 如果是IotException，打印AWS错误信息
                    if (cause instanceof IotException) {
                        System.err.println(((IotException) cause).awsErrorDetails().errorMessage() );
                    } else {
                        // 其他异常，打印异常信息
                        System.err.println("Unexpected error: " + cause.getMessage());
                    }
                    // 返回null表示获取影子信息失败
                    return null;
                });
        try {
            // 阻塞等待结果
            String s = future.get();
            ObjectMapper mapper = new ObjectMapper();
            JsonNode shadowJson = mapper.readTree(s);
            thingShadowDTO.setShadowJson(shadowJson);
            log.info("future.get:{}", s);
            return thingShadowDTO;
        } catch (InterruptedException | ExecutionException e) {
            // 恢复中断状态
            Thread.currentThread().interrupt();
            // 抛出运行时异常，表示获取设备影子信息时出错
            throw new RuntimeException("Error getting device shadow", e);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 更新设备影子信息
     */
    public void updateThingShadow(ThingShadowDTO thingShadowDTO) {
        SdkBytes data = SdkBytes.fromString(thingShadowDTO.getShadowJson().toString(), StandardCharsets.UTF_8);
        UpdateThingShadowRequest updateThingShadowRequest = UpdateThingShadowRequest.builder()
                .thingName(thingShadowDTO.getThingName())
                .payload(data)
                .build();

        CompletableFuture<UpdateThingShadowResponse> future = getAsyncDataPlaneClient().updateThingShadow(updateThingShadowRequest);
        future.whenComplete((updateResponse, ex) -> {
            if (updateResponse != null) {
                log.info("更新设备影子信息成功！ ThingName:{}", thingShadowDTO.getThingName());
            } else {
                Throwable cause = ex != null ? ex.getCause() : null;
                if (cause instanceof IotException) {
                    System.err.println(((IotException) cause).awsErrorDetails().errorMessage());
                } else if (cause != null) {
                    System.err.println("Unexpected error: " + cause.getMessage());
                } else {
                    System.err.println("更新设备影子信息失败！");
                }
            }
        });
        future.join();
    }
}
